Red [
	Title:   "Red simple testing framework"
	Author:  "Peter W A Wood"
	File: 	 %quick-test.red
	Version: "0.2.0"
	Rights:  "Copyright (C) 2012-2015 Peter W A Wood. All rights reserved."
	License: "BSD-3 - https://github.com/red/red/blob/master/BSD-3-License.txt"
]

;; runnable dir for test scripts
split-base-path: split-path base-path: system/options/path
runnable-dir: to-file rejoin [
	; Handle cases of the test files being loaded from different paths
	either "tests/" = last split-base-path [first split-base-path][base-path] 
	"quick-test/runnable/"
]
make-dir/deep runnable-dir

;; counters
#either any [
	not in system 'state
	not system/state/interpreted?	
][
  qt-run-tests: 0 
  qt-run-asserts: 0
  qt-run-passes: 0
  qt-run-failures: 0
  qt-file-tests: 0 
  qt-file-asserts: 0 
  qt-file-passes: 0 
  qt-file-failures: 0
][
  if not value? 'qt-run-tests [
    qt-run-tests: 0 
    qt-run-asserts: 0
    qt-run-passes: 0
    qt-run-failures: 0
    qt-file-tests: 0 
    qt-file-asserts: 0 
    qt-file-passes: 0 
    qt-file-failures: 0
  ]  
]
qt-file-name: none
qt-verbose: false

;; temp file for tests involving `write` and `save`
qt-temp-file: qt-tmp-file: append copy tmp: dirize %. %testfile.txt
change-dir also what-dir 
  unless attempt [
    write qt-tmp-file "test"                            ;-- test if can write into it
    change-dir tmp                                      ;-- test if can CD into %TMP% (needed by do-file)
  ][
    append clear qt-tmp-file %./testfile.txt            ;-- fallback to local dir if %TMP% is closed
  ]
unset 'tmp
qt-tmp-dir: first split-path qt-tmp-file

;; group switches
qt-group-name-not-printed: true
qt-group?: false

_qt-init-group: func [] [
  qt-group-name-not-printed: true
  qt-group?: false
  qt-group-name: ""
]

qt-init-run: func [] [
  qt-run-tests: 0 
  qt-run-asserts: 0
  qt-run-passes: 0
  qt-run-failures: 0
  _qt-init-group
]

qt-init-file: func [] [
  qt-file-tests: 0 
  qt-file-asserts: 0 
  qt-file-passes: 0 
  qt-file-failures: 0
  _qt-init-group
]

***start-run***: func[
    title [string!]
][
  qt-init-run
  qt-run-name: title
  prin "***Starting*** " 
  print title
]

~~~start-file~~~: func [
  title [string!]
][
  qt-init-file
  prin "~~~started test~~~ "
  print title
  qt-file-name: title
  qt-group?: false
]

===start-group===: func [
  title [string!]
][
  if qt-verbose [
    prin "===group=== "
    print title
  ]
  qt-group-name: title
  qt-group?: true
]

--test--: func [
  title [string!]
][
  if qt-verbose [
    prin "--test-- "
    print title
  ]
  qt-test-name: title
  qt-file-tests: qt-file-tests + 1
]

--assert: func [
  assertion [logic!]
][

  qt-file-asserts: qt-file-asserts + 1
  
  either assertion [
     qt-file-passes: qt-file-passes + 1
  ][
    qt-file-failures: qt-file-failures + 1
    if qt-group? [  
      if qt-group-name-not-printed [
        prin "===group=== "
        print qt-group-name
        qt-group-name-not-printed: false
      ]
    ]
    prin "--test-- " 
    prin qt-test-name
    print " FAILED**************"
  ]
]

--assertf~=: func[
  x           [float!]
  y           [float!]
  e           [float!]
  /local
    diff      [float!]
    e1        [float!]
    e2        [float!]
][
  ;; calculate tolerance to use
  ;;    as e * max (1, x, y)
  either x > 0.0 [
    e1: x * e
  ][
    e1: -1.0 * x * e
  ]
  if e > e1 [e1: e]
  either y > 0.0 [
    e2: y * e
  ][
    e2: -1.0 * y * e
  ]
  if e1 > e2 [e2: e1]

  ;; perform almost equal check
  either x > y [
    diff: x - y
  ][
    diff: y - x
  ]
  either diff > e2 [
    --assert false
  ][
    --assert true
  ]
]
 
===end-group===: func [] [
  _qt-init-group
]

qt-print-totals: func [
  tests     [integer!]
  asserts   [integer!]
  passes    [integer!]
  failures  [integer!]
][
  prin  "  Number of Tests Performed:      " 
  print tests 
  prin  "  Number of Assertions Performed: "
  print asserts
  prin  "  Number of Assertions Passed:    "
  print passes
  prin  "  Number of Assertions Failed:    "
  print failures
  if failures <> 0 [
    print "****************TEST FAILURES****************"
  ]
]

~~~end-file~~~: func [] [
  print ["~~~finished test~~~ " qt-file-name]
  qt-print-totals qt-file-tests qt-file-asserts qt-file-passes qt-file-failures
  print ""
  
  ;; update run totals
  qt-run-passes: qt-run-passes + qt-file-passes
  qt-run-asserts: qt-run-asserts + qt-file-asserts
  qt-run-failures: qt-run-failures + qt-file-failures
  qt-run-tests: qt-run-tests + qt-file-tests
]

***end-run***: func [][
  prin "***Finished*** "
  print qt-run-name
  qt-print-totals qt-run-tests
                  qt-run-asserts
                  qt-run-passes
                  qt-run-failures
]
